classdef SweeperModule < EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule
    %SWEEPERMODULE Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        sweep
    end
    
    methods
        function obj = SweeperModule(server, name)
            %SWEEPERMODULE Construct an instance of this class
            %   Detailed explanation goes here
            obj@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(server, name, 'sweep');
        end
    end
    
    methods
        function initialize(obj)
            initialize@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(obj);
        end
        
        function setSweepParameter(obj, device, node, start, stop, varargin)
            p=inputParser;
            p.addRequired('device', @(x) ismember(x, obj.devices));
            p.addRequired('node', @(x) isa(x, 'ziSweepNode'));
            p.addRequired('start', @isnumeric);
            p.addRequired('stop', @isnumeric);
            p.addParameter('npoint', 100, @(x) x>0);
            p.addParameter('sweep_mode', 0, @ziEnumSweepMode.test);
            p.addParameter('isLog', false, @islogical);
            p.parse(device, node, start, stop, varargin{:});
            
            obj.sweep.device = device;
            obj.sweep.node = node;
            if p.Results.isLog
                obj.sweep.value = logspace(start, stop, p.Results.npoint);
            else
                obj.sweep.value = linspace(start, stop, p.Results.npoint);
            end
            
            obj.setModuleSettingValue(ziSettingsSweeperModule.gridnode, ['/' device '/' node.toString]);
            obj.setModuleSettingValue(ziSettingsSweeperModule.start, start);
            obj.setModuleSettingValue(ziSettingsSweeperModule.stop, stop);
            obj.setModuleSettingValue(ziSettingsSweeperModule.samplecount, p.Results.npoint);
            obj.setModuleSettingValue(ziSettingsSweeperModule.scan, p.Results.sweep_mode);
            obj.setModuleSettingValue(ziSettingsSweeperModule.xmapping, int64(p.Results.isLog));            
        end
        
        function setFilter(obj, varargin)
            p=inputParser;
            p.addParameter('bandwidthcontrol', ziEnumSweepBWMode.Mannual, @ziEnumSweepBWMode.test);
            p.addParameter('order', 4, @(x) ismember(x, 1:8));     % fixed & auto mode
            p.addParameter('bandwidth', 100, @(x) x>0);            % fixed mode
            p.addParameter('maxbandwidth', 1.25e6, @(x) x>0);      % auto mode, Maximal bandwidth used in auto bandwidth mode in [Hz]. The default is 1.25MHz.
            p.addParameter('bandwidthoverlap', false, @islogical); % auto mode, Sets the bandwidth overlap mode (default 0).  0 = Disable; 1 = Enable
            p.addParameter('omegasuppression', 40, @(x) x>0);      % auto mode  Damping in [dB] of omega and 2omega components. Default is 40dB in favor of sweep speed. Use higher value for strong offset values or 3omega measurement methods.
            p.parse(varargin{:});
            
            obj.setModuleSettingValue(ziSettingsSweeperModule.bandwidthcontrol, p.Results.bandwidthcontrol);
            obj.setModuleSettingValue(ziSettingsSweeperModule.order,            p.Results.order);
            obj.setModuleSettingValue(ziSettingsSweeperModule.bandwidth,        p.Results.bandwidth);
            obj.setModuleSettingValue(ziSettingsSweeperModule.maxbandwidth,     p.Results.maxbandwidth);
            obj.setModuleSettingValue(ziSettingsSweeperModule.bandwidthoverlap, p.Results.bandwidthoverlap);
            obj.setModuleSettingValue(ziSettingsSweeperModule.omegasuppression, p.Results.omegasuppression);
            obj.sync();
        end
        
        function obj = setSettling(obj, varargin)
            % set settling parameters
            p = inputParser;
            p.addParameter('settling_time', 0);            % double  Settling time before measurement is performed, in [s]
            p.addParameter('settling_tc', 0);              % double  Settling precision: 5 ~ low precision; 15 ~ medium precision; 50 ~ high precision
            p.addParameter('settling_inaccuracy', 10e-3);  % double     [1e-13, 0.1]
            p.parse(varargin{:});

            obj.setModuleSettingValue(ziSettingsSweeperModule.settling_time,       p.Results.settling_time);
            obj.setModuleSettingValue(ziSettingsSweeperModule.settling_tc,         p.Results.settling_tc);
            obj.setModuleSettingValue(ziSettingsSweeperModule.settling_inaccuracy, p.Results.settling_inaccuracy);
            obj.sync();
        end
        
        function obj = setStatistics(obj, varargin)
            % set statistics
            p = inputParser;
            p.addParameter('averaging_tc', 0);     % double  Min averaging time [tc]: 0 = no averaging (see also time!); 5 ~ low precision; 15 ~ medium precision; 50 ~ high precision;
            p.addParameter('averaging_sample', 0); % int     Min samples to average: 1 = no averaging (if averaging/tc = 0); 
            p.addParameter('averaging_time', 0);   % double  Min averaging time [s]
            p.parse(varargin{:});

            obj.setModuleSettingValue(ziSettingsSweeperModule.averaging_tc,     p.Results.averaging_tc);
            obj.setModuleSettingValue(ziSettingsSweeperModule.averaging_sample, p.Results.averaging_sample);
            obj.setModuleSettingValue(ziSettingsSweeperModule.averaging_time,   p.Results.averaging_time);
            obj.sync();
        end
        
        function obj = setOption(obj, varargin)
            % set Options
            p = inputParser;
            p.addParameter('phaseunwrap', 0); % bool    Enable unwrapping of slowly changing phase evolutions around the +/-180 degree boundary.
            p.addParameter('sincfilter', 0);  % bool    Enables the sinc filter if the sweep frequency s below 50 Hz. This will improve the sweep speed at low frequencies as omega components do not need to be suppressed by the normal low pass filter.
            p.addParameter('awgcontrol', 0);  % bool    Enable AWG control for sweeper. If enabled the sweeper will automatically start the AWG and records the sweep sample based on the even index in hwtrigger.
            p.parse(varargin{:});

            obj.setModuleSettingValue(ziSettingsSweeperModule.phaseunwrap, p.Results.phaseunwrap);
            obj.setModuleSettingValue(ziSettingsSweeperModule.sincfilter,  p.Results.sincfilter);
            obj.setModuleSettingValue(ziSettingsSweeperModule.awgcontrol,  p.Results.awgcontrol);
            obj.sync();
        end
    end
    
    %%
    methods
        function res = run(obj, varargin)
            clear textprogressbar;
            
            p=inputParser;
            p.addParameter('timeout', inf, @(x) x>0);
            p.addParameter('tUpdate', 1.0, @(x) x>0);
            p.addParameter('tSync',0.2, @(x) x>=0);
            p.parse(varargin{:});
            
            obj.sync(p.Results.tSync);
            
            obj.execute();
            t0_measurement = tic;
            textprogressbar( sprintf('%s Acquiring Data,\t', datestr(now,'yyyy-mm-dd HH:MM:SS,FFF')) );
            while ~obj.finished()
                textprogressbar(obj.progress()*100);
                pause(p.Results.tUpdate);
                
                if toc(t0_measurement) > p.Results.timeout 
                    break
                end
                
                obj.assembleData(obj.read(), true); %plot progress
            end
            
            if obj.finished() % normal finishing.
                textprogressbar(100); textprogressbar(' Done.');
            else % break due to timeout
                fprintf('\n'); L.info('SweeperModule::run', sprintf('Timeout, runtime = [%3.2e]s', toc(t0_measurement)));
            end
            
            obj.assembleData(obj.read(), false); % assemble final result (without plot)
            res = EquipmentControl.ZI.ziServer.ziServerData.ziServerSweeperModuleData(obj.measured_data, obj.subscribed_path, obj.sweep);
            obj.finish();
        end
        
        function subscribe(obj, device, node,varargin)
            L = log4m.getLogger;
            p=inputParser;
            p.addRequired('device', @(x) ismember(x, obj.devices));
            p.addRequired('node', @(x) isa(x, 'ziStreamNode'));
            p.addOptional('group', [1, 1, 1], @(x) isnumeric(x) && length(x) == 3 && x(3)<=x(1)*x(2));
            p.addParameter('tSync',0.2, @(x) x>=0);
            p.parse(device, node, varargin{:});
            
            rate = obj.enableDataTransfer(device, node);
            L.info('SweeperModule::subscribe', sprintf('Data Transfer Rate = %5.3e Sa/s', rate));
            
            obj.subscribed_path = [obj.subscribed_path; {device, node, p.Results.group}];
            subscribe@EquipmentControl.ZI.ziServer.ServerModules.AbstractServerModule(obj, ['/' device '/' node.toString]);
            obj.sync(p.Results.tSync);
        end
    end
    
    %%
    methods(Access=private)
        function assembleData(obj, tmp, isPlot)
            if isDataAvailable(obj, tmp)
                obj.measured_data = {tmp};
                if isPlot
                    tmpData = EquipmentControl.ZI.ziServer.ziServerData.ziServerSweeperModuleData(obj.measured_data, obj.subscribed_path, obj.sweep);
                    tmpData.plot_data_group(obj.monitor_hdl);
                end
            end
        end
        
        function tf = isDataAvailable(obj, tmpData)
            tf = any(cellfun(@(device, node) ziCheckPathInData(tmpData, ['/' device '/' node.toSwprString]), obj.subscribed_path(:,1), obj.subscribed_path(:, 2)));
        end
    end
end

